package org.chen.spring.core.factory;

import org.chen.spring.ChenApplicationContext;
import org.chen.spring.core.annotation.Autowired;
import org.chen.spring.core.annotation.Component;
import org.chen.spring.core.annotation.ComponentScan;
import org.chen.spring.core.processor.BeanPostProcessor;

import java.beans.Introspector;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Bean工厂
 *
 * @author Chen
 * @since 2023/6/7 12:16
 */
public class BeanFactory {

    private static final BeanFactory BEAN_FACTORY = ChenApplicationContext.getBeanFactory();

    /**
     * Bean信息集合
     */
    private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    /**
     * 单例池
     */
    private final ConcurrentHashMap<String, Object> singletonMap = new ConcurrentHashMap<>();

    private final List<ScanBean> scanBeanList = new ArrayList<>();;

    private final ArrayList<BeanPostProcessor> beanPostProcessorsList = new ArrayList<>();

    public static void registerBeanPostProcessors(BeanPostProcessor beanPostProcessor) {
        BEAN_FACTORY.beanPostProcessorsList.add(beanPostProcessor);
    }

    public static void resisterScanBean(ScanBean scanBean) {
        BEAN_FACTORY.scanBeanList.add(scanBean);
    }


    public static ConcurrentHashMap<String, BeanDefinition> getBeanDefinitionMap() {
        return BEAN_FACTORY.beanDefinitionMap;
    }

    /**
     * 注册Bean，将Bean信息创建加入到BeanDefinitionMap中
     */
    public static void registerBean(String beanName, Class<?> bean) {
        beanName = Introspector.decapitalize(beanName);
        BEAN_FACTORY.beanDefinitionMap.put(beanName, new BeanDefinition(bean, "singleton"));
    }

    /**
     * 注册Bean，将Bean信息保留加入到BeanDefinitionMap中
     */
    public static void registerBean(String beanName, Object bean) {
        beanName = Introspector.decapitalize(beanName);
        BEAN_FACTORY.beanDefinitionMap.put(beanName, new BeanDefinition(bean.getClass(), "singleton"));
        BEAN_FACTORY.singletonMap.put(beanName, bean);
    }

    public void initBean(Class<?> clazz) {
        //解析ComponentScan注解
        String componentScan = clazz.getPackage().getName();
        List<String> annoComponentScans = new ArrayList<>();
        for (Annotation annotation : clazz.getAnnotations()) {
            Class<? extends Annotation> annotationType = annotation.annotationType();
            if (annotationType == ComponentScan.class) {
                annoComponentScans.addAll(Arrays.asList(((ComponentScan) annotation).value()));
            } else if (annotationType.isAnnotationPresent(ComponentScan.class)) {
                annoComponentScans.addAll(Arrays.asList(annotationType.getAnnotation(ComponentScan.class).value()));
            }
        }
        if (!annoComponentScans.isEmpty()) {
            for (String annoComponentScan : annoComponentScans) {
                //扫描路径下的所有类
                if (annoComponentScan != null && !"".equals(annoComponentScan)) {
                    annoComponentScans.add(componentScan);
                }
            }
        } else {
            annoComponentScans.add(componentScan);
        }
        resisterScanBean(new BeanScanBean("org.chen.spring"));

        //注入所有Bean扫描类

        for (ScanBean scanBean : scanBeanList) {
            scanBean.scan(annoComponentScans);
        }

        //实例化单例Bean
        for (BeanDefinition bean : beanDefinitionMap.values()) {
            if ("singleton".equals(bean.getScope())) {
                String name = bean.getType().getSimpleName();
                name = Introspector.decapitalize(name);
                if (singletonMap.containsKey(name)) {
                    singletonMap.put(name, createBean(name, bean));
                }
            }
        }
    }


    public <T> T getBean(String beanName) {
        beanName = Introspector.decapitalize(beanName);
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new RuntimeException("创建名为" + beanName + "的bean失败，原因：没有找到对应的bean定义");
        }
        boolean isSingleton = "singleton".equals(beanDefinition.getScope());
        if (isSingleton) {
            Object bean = singletonMap.get(beanName);
            if (bean == null) {
                bean = createBean(beanName, beanDefinition);
                singletonMap.put(beanName, bean);
            }
            return (T) bean;
        }
        return (T) createBean(beanName, beanDefinition);
    }

    public <T> T getBean(Class<?> clazz) {
        Map<String, T> beansOfType = getBeansOfType(clazz);
        if (beansOfType.isEmpty()) {
            throw new RuntimeException("创建名为" + clazz.getName() + "的bean失败，原因：没有找到对应的bean定义");
        } else if (beansOfType.size() > 1) {
            throw new RuntimeException("创建名为" + clazz.getName() + "的bean失败，原因：找到多个对应的bean定义");
        }
        return beansOfType.values().stream().findFirst().get();
    }

    public <T> Map<String, T> getBeansOfType(Class<?> clazz) {
        HashMap<String, T> resultMap = new HashMap<>();
        for (BeanDefinition value : beanDefinitionMap.values()) {
            if (clazz.isAssignableFrom(value.getType())) {
                Object bean = getBean(Introspector.decapitalize(value.getType().getSimpleName()));
                resultMap.put(getBeanName(value), (T) bean);
            }
        }
        return resultMap;
    }

    private String getBeanName(BeanDefinition beanDefinition) {
        for (Annotation annotation : beanDefinition.getType().getAnnotations()) {
            if (annotation instanceof Component) {
                String value = ((Component) annotation).value();
                if (!"".equals(value)) {
                    return value;
                }
            } else if (annotation.annotationType().isAnnotationPresent(Component.class)) {
                return annotation.annotationType().getAnnotation(Component.class).value();
            }
        }
        return Introspector.decapitalize(beanDefinition.getType().getSimpleName());
    }


    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Object o = singletonMap.get(beanName);
        if (o != null) {
            return o;
        }
        Class<?> clazz = beanDefinition.getType();
        try {
            Object instance = clazz.getConstructor().newInstance();
            //依赖注入
            for (Field field : clazz.getDeclaredFields()) {
                //如果带有Autowired注解，则注入需要的依赖
                immitField(instance, field);
            }

            //BeanPostProcessor前置处理
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorsList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            //BeanPostProcessor后置处理 例如: AOP
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorsList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
                for (Field field : instance.getClass().getSuperclass().getDeclaredFields()) {
                    immitField(instance, field);
                }
            }

            return instance;
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
                 InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    private void immitField(Object instance, Field field) throws IllegalAccessException {
        //如果带有Autowired注解，则注入需要的依赖
        if (field.isAnnotationPresent(Autowired.class)) {
            field.setAccessible(true);
            Object bean;
            try {
                bean = getBean(field.getName());
            } catch (Exception e) {
                bean = getBean(field.getType());
            }
            field.set(instance, bean);
        }
    }
}
